package web

import (
	"context"
	"encoding/json"
	"fmt"
	"gitee.com/xfrm/middleware/xlog"
	"gitee.com/xfrm/structs"
	"net/http"
	"reflect"
	"strconv"
	"strings"
)

const XFRMTokenHeader = "X-Frm-Token"
const XFRMAuthHeader = "X-Frm-Authentication"

// XFRMAuthInfo
type XFRMAuthInfo struct {
	//用户id标识
	UserId int64 `json:"user-id"`
	//授权类型
	AuthType EnumAuthType `json:"auth-type"`
	//产品标识
	Source EnumSource `json:"source"`
	//设备id
	Did string `json:"device-id"`
	//授权扩展信息
	Principal string `json:"principal"`
} //@name 授权详情

func (m XFRMAuthInfo) ToHeaderValue() string {
	return ToHeaderValue(m)
}

var tag_head = "json"

func ToHeaderValue(o interface{}) string {
	oStruct := structs.New(o)
	oStruct.TagName = tag_head
	var vals string
	for i, field := range oStruct.Fields() {
		k, _ := field.Tag(tag_head)
		if k == "" {
			return k
		}
		if i != 0 {
			vals += "; "
		}
		vals += fmt.Sprintf("%s=%v", k, field.Value())
	}
	return vals
}

func ParseXFRMAuthInfo(ctx context.Context, raw string) (XFRMAuthInfo, error) {
	var item XFRMAuthInfo
	err := ParseXFRMHeader(ctx, raw, &item)
	if err != nil {
		return XFRMAuthInfo{}, err
	}
	return item, nil
}
func GetRawXFRMAuthInfoFromRequest(ctx context.Context, req *http.Request) string {
	return req.Header.Get(XFRMAuthHeader)
}
func GetXFRMAuthInfoFromRequest(ctx context.Context, req *http.Request) (XFRMAuthInfo, error) {
	raw := GetRawXFRMAuthInfoFromRequest(ctx, req)
	return ParseXFRMAuthInfo(ctx, raw)
}

func ParseXFRMHeader(ctx context.Context, raw string, item interface{}) error {
	parts := strings.Split(raw, ";")
	var filteredParts []string
	for _, p := range parts {
		if p != "" {
			filteredParts = append(filteredParts, p)
		}
	}
	oStruct := structs.New(item)
	var filedMap = make(map[string]*structs.Field)
	for _, f := range oStruct.Fields() {
		key, _ := f.Tag(tag_head)
		if key == "" {
			continue
		}
		filedMap[key] = f
	}
	kvMap := make(map[string]interface{})
	for _, p := range filteredParts {
		kvpair := strings.Split(p, "=")
		if len(kvpair) != 2 {
			msg := fmt.Sprintf("no `=` found for kvpair: %s", p)
			return NewInvalidXFRMHeaderError(msg)
		}
		k, v := strings.TrimSpace(kvpair[0]), strings.TrimSpace(kvpair[1])
		field, ok := filedMap[k]
		if !ok || field == nil {
			continue
		}
		var val interface{}
		//只处理 数字、字符串、bool型的属性更新
		switch field.Kind() {
		case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int8:
			va, err := strconv.ParseInt(v, 10, 64)
			if err != nil {
				xlog.Warnf(ctx, "parse int in header err: v: %s, err: %v", v, err)
				continue
			}
			val = va
		case reflect.String:
			val = v
		case reflect.Bool:
			v = strings.ToLower(v)
			val = v == "true"
		default:
			xlog.Warnf(ctx, "unsupport type: v %v,", v)
			continue
		}
		kvMap[k] = val
	}

	data, err := json.Marshal(kvMap)
	if err != nil {
		return err
	}
	err = json.Unmarshal(data, &item)
	if err != nil {
		return err
	}
	return nil
}

type InvalidXFRMHeaderError struct {
	msg string
}

func NewInvalidXFRMHeaderError(msg string) error {
	return &InvalidXFRMHeaderError{
		msg: msg,
	}
}
func (e *InvalidXFRMHeaderError) Error() string {
	return fmt.Sprintf("ErrInvalidXFRMHeaderError(msg=%s)", e.msg)
}

//把鉴权后的信息写入到header中，
//这一步是在网关中来实现，此处是简单实现，具体网关需要根据自己的实现重写
func Authenticate(ctx context.Context, req *http.Request, authFunc func(ctx2 context.Context, tk string) (XFRMAuthInfo, error)) (XFRMAuthInfo, error) {
	token := req.Header.Get(XFRMTokenHeader)
	authInfo, err := authFunc(ctx, token)
	if err != nil {
		return XFRMAuthInfo{}, err
	}
	req.Header.Set(XFRMAuthHeader, ToHeaderValue(&authInfo))
	return authInfo, nil
}

func SetAuthentication(ctx context.Context, res http.ResponseWriter, atkFunc func(ctx2 context.Context) string) error {
	res.Header().Add(XFRMTokenHeader, atkFunc(ctx))
	return nil
}
